import 'dart:math';

import 'package:flutter/material.dart';

/* key的分类：
*  1.LocalKey：用于相同父Element内部的多个小部件进行比较是否需要进行移动处理：ValueKey、ObjectKey、UniqueKey
*  2.GlobalKey 使用了一个静态常量 Map 来保存它对应的 Element。通过 GlobalKey 找到持有该GlobalKey的 Widget，State 和 Element
*
* key可以让element强制刷新，也可以用于判断复用哪一个element
*/
void main() => runApp(KeyDemoApp());

class KeyDemoApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home:
          // GlobalKeyDemo()
          //     KeyDemo2()
          KeyDemo1(),
    );
  }
}

// key demo 1
// 每次setState都会重新执行build方法，如果是StatelessWidget build方法在widget中执行
// StatefulWidget的build方法在State中执行
class KeyDemo1 extends StatefulWidget {
  KeyDemo1({Key key}) : super(key: key);

  @override
  _KeyDemo1State createState() => _KeyDemo1State();
}

class _KeyDemo1State extends State<KeyDemo1> {
  List<Widget> widgets = [
    // 当给StatefulContainer传入key参数时，交换两个元素位置才会发生改变，不传则不会改变
    StatefulContainer(
      key: UniqueKey(),
    ),
    StatefulContainer(
      key: UniqueKey(),
    ),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: widgets,
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _switchChild,
        child: Icon(Icons.swap_horizontal_circle),
      ),
    );
  }

  _switchChild() {
    widgets.insert(0, widgets.removeAt(1));
    setState(() {});
  }
}

// 使用StatelessWidget点击交换按钮会发生变化
class StatelessContainer extends StatelessWidget {
  final Color color = _randomColor();

  @override
  Widget build(BuildContext context) {
    var container = Container(
      width: 50,
      height: 50,
      color: color,
    );
    print('create widget');
    return container;
  }
}

// 使用StatelessWidget点击交换按钮默认不会发生变化
class StatefulContainer extends StatefulWidget {
  StatefulContainer({Key key}) : super(key: key);

  @override
  _StatefulContainerState createState() => _StatefulContainerState();
}

class _StatefulContainerState extends State<StatefulContainer> {
  final Color color = _randomColor();

  @override
  void initState() {
    super.initState();
    print('initState');
  }

  @override
  Widget build(BuildContext context) {
    var container = Container(
      width: 50,
      height: 50,
      color: color,
    );
    print('create widget');
    return container;
  }
}

Color _randomColor() {
  print('_randomColor');
  return Color.fromARGB(255, Random().nextInt(256) + 0,
      Random().nextInt(256) + 0, Random().nextInt(256) + 0);
}

// key demo2
class KeyDemo2 extends StatefulWidget {
  @override
  _KeyDemo2State createState() => _KeyDemo2State();
}

class _KeyDemo2State extends State<KeyDemo2> {
  final List<String> names = ["aaa", "bbb", "ccc"];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.delete),
        // 点击删除一条Widget
        onPressed: () {
          setState(() {
            names.removeAt(0);
          });
        },
      ),
      body: ListView(
        children: names
            .map((e) => ListItemFulKey(
                  e,
                  key: ValueKey(e),
                ))
            .toList(),
      ),
    );
  }
}

// 使用StatelessWidget测试
// 每次点击删除不仅仅删除了一个Widget而且剩下的Widget颜色发生改变
// 调用setState方法后，需要重新build，所有widget也会重建，最终生成新的widget，所以item的背景色也会改变
class ListItemLess extends StatelessWidget {
  final String name;

  ListItemLess(this.name);

  final Color color = Color.fromARGB(
      255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256));

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 100,
      color: color,
      child: Text(name),
    );
  }
}

// StatefulWidget测试
// 结果是颜色虽然还是原来的三种颜色，但是对应的Text内容改变，原因是复用导致
// 我们写的Widget树生成对应的Element树，判断会不会生成对应Element：Flutter调用canUpdate方法传入oldWidget、newWidget
// 当两者运行时类型相同，并且key相同则不会生成对应的Element
// 由于我们的Widget没有发生变化，Element会直接保留下来不会重建，里面持有的state也没有重建。删除第一个widget(aaa)后，
// bbb、ccc复用直接挂载aaa的widget，而最后一个颜色没有和widget挂载，直接删除(默认删除最后一个)

// 如果要求删除一个widget之后重建element而不是复用，就可以指定不同的key，即强制刷新element
class ListItemFul extends StatefulWidget {
  final String name;

  ListItemFul(this.name);

  @override
  _ListItemFulState createState() => _ListItemFulState();
}

class _ListItemFulState extends State<ListItemFul> {
  final Color color = Color.fromARGB(
      255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256));

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 100,
      color: color,
      child: Text(widget.name),
    );
  }
}

// 要求一个state对应一个widget，保证内容和颜色一起删除不复用，就需要指定key参数
// 当删除一个widget后，会有两个Element tree，原来的三个widget的树和剩下的两个widget的树
// 两者相互比较 diff算法，判断需不需要重新创建Element tree
// 加上key绑定之后则不会删除最后一个element，而是通过key比较，删除没有对应key的element
class ListItemFulKey extends StatefulWidget {
  final String name;

  ListItemFulKey(this.name, {Key key}) : super(key: key);

  @override
  _ListItemFulKeyState createState() => _ListItemFulKeyState();
}

class _ListItemFulKeyState extends State<ListItemFulKey> {
  final Color color = Color.fromARGB(
      255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256));

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 100,
      color: color,
      child: Text(widget.name),
    );
  }
}

// GlobalKey 测试,它可以帮助我们访问widget信息，例如Element信息、State信息等
class GlobalKeyDemo extends StatelessWidget {
  // 定义GlobalKey
  final GlobalKey<_GlobalKeyContentState> homeKey = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: GlobalKeyContent(
        key: homeKey,
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.cached),
        onPressed: () {
          // 点击获取Widget、State中的信息
          print('${homeKey.currentState.widget.name}');
          print('${homeKey.currentState.msg}');
          // 直接调用state中的方法
          homeKey.currentState.test();
        },
      ),
    );
  }
}

class GlobalKeyContent extends StatefulWidget {
  final String name = "fauks";

  @override
  _GlobalKeyContentState createState() => _GlobalKeyContentState();

  GlobalKeyContent({Key key}) : super(key: key);
}

class _GlobalKeyContentState extends State<GlobalKeyContent> {
  final String msg = "123";

  test() {
    print('_GlobalKeyContentState test');
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Text(msg),
    );
  }
}
